use stencil_tysys::ValueType;
use stencil_util::{FieldName, TypeName};

use super::common::{TypeFamily, Typedef};
use crate::{field::FieldOp, ArrayShape};

/// Description of a struct type.  These have a buffer of a static size.
#[derive(Clone, Debug)]
pub struct StructBufTd {
    name: TypeName,

    /// The fixed struct buffer size that we generate for this type.
    buf_size: u64,

    /// These fields all get projected into the buffer.
    fields: Vec<StructBufTdField>,
}

impl StructBufTd {
    pub fn new(name: TypeName, buf_size: u64, fields: Vec<StructBufTdField>) -> Self {
        Self {
            name,
            buf_size,
            fields,
        }
    }

    pub fn buf_size(&self) -> u64 {
        self.buf_size
    }

    pub fn fields(&self) -> &[StructBufTdField] {
        &self.fields
    }
}

impl Typedef for StructBufTd {
    fn name(&self) -> &TypeName {
        &self.name
    }

    fn family(&self) -> TypeFamily {
        TypeFamily::Buf
    }
}

/// A field within a buffer.
#[derive(Clone, Debug)]
pub struct StructBufTdField {
    name: FieldName,

    /// The base type of the field.
    unit_ty: ValueType,

    /// The shape of the field.
    ///
    /// This is "just" an array shape instead of a field shape since we can
    /// never have sequences here.  A unit array shape works here.
    shape: ArrayShape,

    /// The offset of the field within the buffer.
    ///
    /// Struct fields always have static offsets.
    offset: u64,

    /// Operations we want to expose for this field.
    ops: Vec<FieldOp>,
}

impl StructBufTdField {
    pub fn new(
        name: FieldName,
        unit_ty: ValueType,
        shape: ArrayShape,
        offset: u64,
        ops: Vec<FieldOp>,
    ) -> Self {
        Self {
            name,
            unit_ty,
            shape,
            offset,
            ops,
        }
    }

    pub fn name(&self) -> &FieldName {
        &self.name
    }

    pub fn unit_ty(&self) -> &ValueType {
        &self.unit_ty
    }

    pub fn shape(&self) -> &ArrayShape {
        &self.shape
    }

    pub fn offset(&self) -> u64 {
        self.offset
    }

    pub fn ops(&self) -> &[FieldOp] {
        &self.ops
    }
}
